////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "DynamicLibrary.hpp"
#include "IOFileException.hpp"
#include <iostream>
#include <sstream>

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace Toolkit {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace Exceptions;

DynamicLibrary::DynamicLibrary()
: file_name(""), handle(NULL)
{}

DynamicLibrary::DynamicLibrary(const DynamicLibrary & inst)
: file_name(inst.file_name), handle(inst.handle)
{}

DynamicLibrary::~DynamicLibrary() {
	if (this->IsLoaded())
		this->Detach();
}

DynamicLibrary & DynamicLibrary::operator = (const DynamicLibrary & inst) {
	if (this != &inst) {
		this->file_name	= inst.file_name;
		this->handle = inst.handle;	
	}
	return *this;
}

void DynamicLibrary::Attach(const string & file_name) {
	// Open the library
#ifdef __UNIX__
	this->handle = dlopen(file_name.c_str(),RTLD_LAZY);
#else
#ifdef __MSW__
	this->handle = LoadLibrary(file_name.c_str());
#endif
#endif
	// Check it is opened
	if (handle == NULL)
		throw IOFileException("Cannot open library \"" + file_name + "\" : " + this->getLastError());
	// Save file name
	this->file_name = file_name;
}

const bool DynamicLibrary::IsLoaded() { return this->handle != NULL; }

void DynamicLibrary::Detach() {
	if (!this->IsLoaded())
		throw IOFileException("Cannot close library : No loaded library");
	/// Close the library
	bool ok;
#ifdef __UNIX__
	ok = (dlclose(this->handle) == 0);
#else
#ifdef __MSW__
	ok = (FreeLibrary(this->handle) != 0);
#endif
#endif
	if (!ok)
		throw IOFileException("Cannot close library \"" + this->file_name + "\" : " + this->getLastError());
	// Delete old information
	this->file_name = "";
	this->handle = NULL;
}

FuncPtr DynamicLibrary::GetFunction(const string & func_name) {
	FuncPtr result =
#ifdef __UNIX__
		dlsym
#else
#ifdef __MSW__
		(FuncPtr)GetProcAddress
#endif
#endif
			(this->handle,func_name.c_str());
	return result;
}

const string DynamicLibrary::getLastError() {
#ifdef __UNIX__
	return string(dlerror());
#else
#ifdef __MSW__
	LPVOID lpMsgBuf;
	DWORD dw = GetLastError();
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR) &lpMsgBuf,
		0,
		NULL
	);
	stringstream ss;
	ss << dw << " : " << (char*)lpMsgBuf;
	LocalFree(lpMsgBuf);
	return ss.str();
#endif
#endif	
}

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////
